package userlogic

import (
	"context"
	"strings"

	"go-zero-admin/apps/system/cmd/rpc/internal/logic/menu"
	"go-zero-admin/apps/system/cmd/rpc/internal/logic/role"
	"go-zero-admin/apps/system/cmd/rpc/internal/logic/userscope"
	"go-zero-admin/apps/system/cmd/rpc/internal/svc"
	"go-zero-admin/apps/system/cmd/rpc/syspb"
	"go-zero-admin/pkg/constant"

	"github.com/duke-git/lancet/v2/convertor"
	"github.com/zeromicro/go-zero/core/fx"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetUserRulesLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
	getRoleByUidLogic     *rolelogic.GetRoleByUidLogic
	getMenuListLogic      *menulogic.GetMenuListLogic
	getRoleIdsByUidLogic  *rolelogic.GetRoleIdsByUidLogic
	getUserDataScopeLogic *userscopelogic.GetUserDataScopeLogic
}

func NewGetUserRulesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserRulesLogic {
	return &GetUserRulesLogic{
		ctx:                   ctx,
		svcCtx:                svcCtx,
		Logger:                logx.WithContext(ctx),
		getRoleByUidLogic:     rolelogic.NewGetRoleByUidLogic(ctx, svcCtx),
		getMenuListLogic:      menulogic.NewGetMenuListLogic(ctx, svcCtx),
		getRoleIdsByUidLogic:  rolelogic.NewGetRoleIdsByUidLogic(ctx, svcCtx),
		getUserDataScopeLogic: userscopelogic.NewGetUserDataScopeLogic(ctx, svcCtx),
	}
}

func (l *GetUserRulesLogic) GetUserRules(in *syspb.GetUserRulesReq) (*syspb.GetUserRulesResp, error) {
	if in.GetId() == 0 {
		return &syspb.GetUserRulesResp{Tree: nil, Permissions: nil}, nil
	}

	// 获取菜单数据
	listResp, err := l.getMenuListLogic.GetMenuList(&syspb.GetMenuListReq{})
	if err != nil {
		return nil, err
	}
	if len(listResp.GetList()) == 0 {
		return &syspb.GetUserRulesResp{Tree: nil, Permissions: nil}, nil
	}

	// 超管获取所有菜单
	if l.getUserDataScopeLogic.IsAdminUser(in.GetId()) {
		return &syspb.GetUserRulesResp{Tree: l.getMenuTreeList(listResp.GetList()), Permissions: []string{"*/*/*"}}, nil
	}

	// 非超管数据
	// 获取用户角色数据
	roleIdsResp, err := l.getRoleIdsByUidLogic.GetRoleIdsByUid(
		&syspb.GetRoleIdsByUidReq{UserId: in.GetId()})
	if err != nil {
		return nil, err
	}
	roleIds := roleIdsResp.GetRoleIds()
	if len(roleIds) == 0 {
		return &syspb.GetUserRulesResp{Tree: nil, Permissions: nil}, nil
	}

	// 获取菜单和按钮权限
	return &syspb.GetUserRulesResp{
		Tree:        l.getUserMenuListByRids(roleIds, listResp.GetList()),
		Permissions: l.getPermissions(roleIds, listResp.GetList()),
	}, nil
}

// 获取菜单和目录的菜单树
func (l *GetUserRulesLogic) getMenuTreeList(list []*syspb.SysMenu) []*syspb.SysMenuTree {
	var menuList []*syspb.SysMenu
	for _, v := range list {
		if v.GetMenuType() == constant.MenuTypeBtn {
			continue
		}

		menuList = append(menuList, v)
	}
	if len(menuList) == 0 {
		return nil
	}

	var menuTree []*syspb.SysMenuTree
	for _, v := range menuList {
		menuTree = append(menuTree, &syspb.SysMenuTree{Menu: v})
	}

	return l.getMenuTree(menuTree, constant.MenuFirstPid)
}

// 根据角色获取用户菜单数据
func (l *GetUserRulesLogic) getUserMenuListByRids(roleIds []int64, menuList []*syspb.SysMenu) []*syspb.SysMenuTree {
	enforcer := l.svcCtx.AdapterCasbin.SetCtxSession(l.ctx, nil).Enforcer

	// 获取角色对应的菜单id
	menuIds := map[int64]int64{}
	fx.From(func(source chan<- interface{}) {
		for _, v := range roleIds {
			source <- v
		}
	}).Map(func(item interface{}) interface{} {
		return enforcer.GetFilteredPolicy(0, convertor.ToString(item))
	}).ForEach(func(item interface{}) {
		for _, v := range item.([][]string) {
			mid, _ := convertor.ToInt(v[1])
			menuIds[mid] = mid
		}
	})
	if len(menuIds) == 0 {
		return nil
	}

	var menuPartList []*syspb.SysMenu
	for _, v := range menuList {
		if v.GetMenuType() == constant.MenuTypeBtn {
			continue
		}
		menuPartList = append(menuPartList, v)
	}
	if len(menuPartList) == 0 {
		return nil
	}

	// 获取用户菜单
	var menuTree []*syspb.SysMenuTree
	for _, v := range menuPartList {
		if _, ok := menuIds[v.GetId()]; strings.EqualFold(v.GetCondition(), "nocheck") || ok {
			menuTree = append(menuTree, &syspb.SysMenuTree{Menu: v})
		}
	}
	if len(menuTree) == 0 {
		return nil
	}

	return l.getMenuTree(menuTree, constant.MenuFirstPid)
}

// 根据角色获取用户按钮数据
func (l *GetUserRulesLogic) getPermissions(roleIds []int64, menuList []*syspb.SysMenu) []string {
	enforcer := l.svcCtx.AdapterCasbin.SetCtxSession(l.ctx, nil).Enforcer

	// 获取角色对应的菜单id
	menuIds := map[int64]int64{}
	fx.From(func(source chan<- interface{}) {
		for _, v := range roleIds {
			source <- v
		}
	}).Map(func(item interface{}) interface{} {
		return enforcer.GetFilteredPolicy(0, convertor.ToString(item))
	}).ForEach(func(item interface{}) {
		for _, v := range item.([][]string) {
			mid, _ := convertor.ToInt(v[1])
			menuIds[mid] = mid
		}
	})
	if len(menuIds) == 0 {
		return nil
	}

	// 获取所有开启的按钮
	var buttonList []*syspb.SysMenu
	fx.From(func(source chan<- interface{}) {
		for _, v := range menuList {
			source <- v
		}
	}).Filter(func(item interface{}) bool {
		sysMenu := item.(*syspb.SysMenu)
		return sysMenu.MenuType == constant.MenuTypeBtn
	}).ForEach(func(item interface{}) {
		buttonList = append(buttonList, item.(*syspb.SysMenu))
	})
	if len(buttonList) == 0 {
		return nil
	}

	var userButtons []string
	for _, button := range buttonList {
		if _, ok := menuIds[button.GetId()]; strings.EqualFold(button.Condition, "nocheck") || ok {
			userButtons = append(userButtons, button.Name)
		}
	}
	return userButtons
}

// 组装树状结构
func (l *GetUserRulesLogic) getMenuTree(menus []*syspb.SysMenuTree, pid int64) []*syspb.SysMenuTree {
	var returnList []*syspb.SysMenuTree
	for _, menu := range menus {
		if menu.GetMenu().GetPid() != pid {
			continue
		}

		menu.Children = l.getMenuTree(menus, menu.GetMenu().GetId())

		returnList = append(returnList, menu)
	}
	return returnList
}
